/*
 * ConsoleManager.c
 *
 *  Created on: Mar 12, 2012
 *      Author: mikaelgra
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "ConsoleManager.h"
#include "AlarmManager.h"
#include "ConsoleWorkItem.h"
#include "ArrayList.h"
#include "gpio/Define.h"

/**
 * WorkItem defines identifying each individual
 * type of WorkItem. A WorkItem type represents
 * a unit of work that the user has to do. For
 * example entering the password of a triggered
 * alarm.
 */
#define WORKITEM_DEFAULT 1

/*
 * Method prototyping. These are the "private" methods
 */
void ConsoleManager_MainLoop();
void ConsoleManager_GetInputFromConsole(struct stConsoleWorkItem_t *workItem);
void ConsoleManager_Process(struct stConsoleWorkItem_t *workItem);

/**
 * ArrayList of all the WorkItems that the user
 * has queue up to work on.
 */
struct stArrayList_t *ConsoleWorkItems = NULL;

/**
 * Boolean indicating whether or not the ConsoleManager
 * is currently running.
 */
bool_t IsRunning;

/**
 * Initializes the ConsoleManager. This must be called before
 * any other methods on the ConsoleManager.
 */
void ConsoleManager_Initialize()
{
	IsRunning = FALSE;
	ConsoleWorkItems = ArrayList_New(ArrayList_AddressComparison);
}

/**
 * Starts the ConsoleManager. This is a synchronous call
 * that wont return until the ConsoleManager has been stopped.
 */
void ConsoleManager_Start()
{
	IsRunning = TRUE;
	ConsoleManager_MainLoop();
}

/**
 * Stops the ConsoleManager by ensuring that no more WorkItems
 * will be processed and that the thread responsible returns.
 */
void ConsoleManager_Stop()
{
	IsRunning = FALSE;
}

/**
 * This method is used as the main logic loop of the ConsoleManager.
 */
void ConsoleManager_MainLoop()
{
	while(IsRunning)
	{
		int isEmpty = ArrayList_IsEmpty(ConsoleWorkItems);
		if(isEmpty)
		{
			// default work item
			char output[512];
			strcpy(output, "Alarms:\n");

			int i;
			for(i = 0; i < AlarmManager_GetAlarmCount(); i++)
			{
				struct stAlarm_t* alarm = AlarmManager_GetAlarmAt(i);
				strcat(output, "  ");
				strcat(output, alarm->name);
				if(alarm->enAlarmState == eEnabled)
				{
					strcat(output, " - Enabled");
				}
				else if(alarm->enAlarmState == eDisabled)
				{
					strcat(output, " - Disabled");
				}
				else if(alarm->enAlarmState == eTriggered)
				{
					strcat(output, " - Triggered ");

					char triggerTimer[10];
					sprintf(triggerTimer, "%d/%d", alarm->timeSinceTrigger, alarm->triggerTime);
					strcat(output, triggerTimer);
				}
				else if(alarm->enAlarmState == eStarted)
				{
					strcat(output, " - Started ");

					// add current time since triggered
					// (THIS HAS NOT BEEN TESTED!!!)
				}

				strcat(output, ", ON GPIO ");
				char gpio[3];
				sprintf(gpio, "%d", alarm->gpioPin);
				strcat(output, gpio);
				strcat(output, "\n");
			}
			strcat(output, "\n");

			struct stConsoleWorkItem_t* item = ConsoleWorkItem_New(output, TRUE, WORKITEM_DEFAULT);

			// process
			ConsoleManager_GetInputFromConsole(item);
			ConsoleManager_Process(item);

			// finalize
			if(IsRunning)
			{
				ConsoleWorkItem_Finalize(item);
			}
		}
		else
		{
			// retrieve
			struct stConsoleWorkItem_t* item = (struct stConsoleWorkItem_t*)ArrayList_Get(ConsoleWorkItems, 0);

			// process
			ConsoleManager_GetInputFromConsole(item);
			ConsoleManager_Process(item);

			// finalize
			if(IsRunning)
			{
				// remove item from array list (may not be indexed 0)
				// because an item of higher priority may have come
				// in the meanwhile, hence we have to remove it by
				// reference and not by index.
				ArrayList_Remove(ConsoleWorkItems, item);
				ConsoleWorkItem_Finalize(item);
			}
		}
	}
}

/**
 * Interrupts the ConsoleManager with a workItem that must be processed
 * next. This will be called when an event happens on an alarm.
 *
 * @param workItem
 * 		This is the ConsoleWorkItem that represents the unit of work
 * 		that must be done at high priority.
 */
void ConsoleManager_InterruptWith(struct stConsoleWorkItem_t *workItem)
{
	// called by Application when AlarmManager notifies
	// somehow need to interrupt current input operation!

	// for now, just add it as an item with high priority

	//ArrayList_Insert(ConsoleWorkItems, 0, workItem);
}

/**
 * Displays the WorkItem in the console and retrieves input from
 * the user if required. The input is placed in the result
 * part of the ConsoleWorkItem.
 *
 * @param workItem
 * 		This is the ConsoleWorkItem that should be displayed
 * 		and that we should retrieve input from.
 */
void ConsoleManager_GetInputFromConsole(struct stConsoleWorkItem_t *workItem)
{
	printf("%s\n> ", workItem->displayText);
	if(workItem->requireInput)
	{
		char result[MAX_RESULT_LENGTH];
		gets(result);
		ConsoleWorkItem_SetResult(workItem, result);
	}
}

/**
 * Processes the ConsoleWorkItem doing whatever is necessary in order
 * to continue processing logic.
 *
 * @param workItem
 * 		This is the ConsoleWorkItem that must be processed.
 */
void ConsoleManager_Process(struct stConsoleWorkItem_t *workItem)
{
	// necessary logic to process user input, maybe create new workItem
	if(workItem->type == WORKITEM_DEFAULT)
	{
		printf("%s\n\n", workItem->result);
	}
}

/**
 * Finalizes the ConsoleManager stopping it, if necessary and
 * releasing all used resources.
 */
void ConsoleManager_Finalize()
{
	ConsoleManager_Stop();

	// destroy any items remaining
	while(!ArrayList_IsEmpty(ConsoleWorkItems))
	{
		struct stConsoleWorkItem_t* item = (struct stConsoleWorkItem_t*)ArrayList_RemoveAt(ConsoleWorkItems, ArrayList_GetSize(ConsoleWorkItems) - 1);
		ConsoleWorkItem_Finalize(item);
	}
}
